/*
 * Copyright (c) 2009-2011, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef ALKES_IMAGE_BUFFER_H_INCLUDED_
#define ALKES_IMAGE_BUFFER_H_INCLUDED_

#include "alkes/internal.h"
#include "alkes/core/TRefCount.h"
#include "alkes/media/PixelFormat.h"
#include "alkes/core/TRect.h"
#include "alkes/core/ClassID.h"
#include "alkes/core/Types.h"

namespace alkes {

class ImageDecoder;
class ImageWriter;

/// \addtogroup media
//@{

enum LockMode
{
    AL_LOCKMODE_READ    = 0x01,
    AL_LOCKMODE_WRITE   = 0x02,
    AL_LOCKMODE_READWRITE = AL_LOCKMODE_READ|AL_LOCKMODE_WRITE,
};

struct ImageBufferCaps
{
    uint16_t image_width;
    uint16_t image_height;
    uint16_t buf_width;
    uint16_t buf_height;
    PixelFormat format;
};

class AL_EXPORT ImageBuffer
    : public TRefCount<ImageBuffer>
{
    AL_DECLARE_ROOT_CLASS(ImageBuffer);

public:

    /** Gets a size of this buffer.

    In some cases, this size differs a size from getImageSize(). This depends on the low graphics library.
    e.g. OpenGL(less than version 2)

    If you get a valid image size, use getImageSize().

    @param [out] width width of image buffer.
    @param [out] height height of image buffer.

    @sa getImageSize()
    */
    virtual void getSize(uint16_t& width, uint16_t& height) const = 0;

    /** Gets a size of this image.

    @param [out] width
    @param [out] height

    @sa getSize()
    */
    virtual void getImageSize(uint16_t& width, uint16_t& height) const;

    /** Returns PixelFormat of this image.

    @return pixel format.

    @sa PixelFormat
    */
    virtual PixelFormat getPixelFormat() const = 0;

    /** Lock image buffer.

    @param [in] rect
    @param [in] lock_mode
    @param [out] pitch
    @retval 0 Failed to lock.
    @retval !0 Address of this buffer.
    */
    void* lockRect(const Rect* rect, uint32_t lock_mode, int32_t* pitch);

    /** Unlock image buffer.
    */
    void unlockRect();

    /** Return a value that indicates whether this buffer is locked.

    @retval true Locked.
    @retval false Not locked.
    */
    bool isLocked() const;

    /** Copy buffer from another.

    @param [in] src
    @param [in] src_rect
    @param [in] dst_x
    @param [in] dst_y
    @return 
    */
    bool copyFrom(ImageBuffer* src, const Rect* src_rect = NULL, uint16_t dst_x = 0, uint16_t dst_y = 0);

    /** Copy buffer from another.

    @param [in] buffer
    @param [in] pitch
    @param [in] traits
    @param [in] dst_rect
    @return 
    */
    bool copyFrom(uint8_t* buffer, int32_t pitch, const PixelFormatTraits* traits, const Rect& dst_rect);

    /** Get the Rect of this buffer.

    @return
    @sa getSize()
    */
    const Rect getRect() const;

    /** Get the Rect of this image.

    @return
    @sa getImageSize()
    */
    const Rect getImageRect() const;

    /** Get a detail information of this buffer.

    @return
    @sa getSize(), getImageSize(), getPixelFormat(), getRect(), getImageRect()
    */
    ImageBufferCaps getCaps() const;

    /** Returns a value that indicates whether this buffer is readable.

    @retval true Readable.
    @retval false Not readable.
    */
    bool isReadable() const;

    /** Returns a value that indicates whether this buffer is writable.

    @retval true Writable.
    @retval false Not writable.
    */
    bool isWritable() const;

    /** Resizes the size of this image.

    @param [in] new_width This parameter must be less than or equal width of buffer.
    @param [in] new_height This parameter must be less than or equal height of buffer.
    @return
    */
    bool resizeImageSize(uint16_t new_width, uint16_t new_height);

    /** Saves this buffer to the specific file.
    @param [in] filepath
    @param [in] rect
    @return
    */
    bool save(const char* filepath, const Rect* rect = 0);

    /** Saves this buffer to the specific writer.
    @param [in] writer
    @param [in] rect
    @return
    */
    bool save(ImageWriter& writer, const Rect* rect = 0);

    /** Clear image buffer.
    */
    bool clear(const Color& color);

    /** Flip image buffer upside-down.
    */
    bool doUpsideDown();

protected:
    friend class TRefCount<ImageBuffer>;

    enum BufferAttribute
    {
        AL_BUFATTR_DENY_READ = 0,
        AL_BUFATTR_DENY_WRITE,
        AL_BUFATTR_LOCK_FOR_READ,
        AL_BUFATTR_LOCK_FOR_WRITE,

        AL_DERIVED_CLASS_USABLE = 8,
    };

    ImageBuffer(bool write_only);
    virtual ~ImageBuffer();
    virtual void destroy() = 0;

    virtual void* onLockRect(const Rect& rect, uint32_t lock_mode, int32_t* pitch) = 0;
    virtual void onUnlockRect(const Rect& locked_rect) = 0;
    virtual bool onResizeImageSize(uint16_t new_width, uint16_t new_height);

    void denyRead();
    void denyWrite();

private:
    Rect locked_rect_;
};

//@}

}

#endif
